package serial;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Formatter;

import javax.swing.JOptionPane;

public class RobokitCommands {
	private static RobokitCommands instance;
	private static OutputStream out;

	public RobokitCommands() throws Exception {
		connect("COM1");
	}

	public static RobokitCommands getInstance() {
		if (instance == null) {
			try {
				instance = new RobokitCommands();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return instance;
	}

	public static void write(byte[] bs) {
		try {
			getInstance().getOutputStream().write(bs);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private OutputStream getOutputStream() {
		return out;
	}

	void connect(String portName) {
		try {
			CommPortIdentifier portIdentifier = CommPortIdentifier
					.getPortIdentifier(portName);
			if (portIdentifier.isCurrentlyOwned()) {
				System.out.println("Error: Port is currently in use");
			} else {
				CommPort commPort = portIdentifier.open(this.getClass()
						.getName(), 2000);

				if (commPort instanceof SerialPort) {
					SerialPort serialPort = (SerialPort) commPort;
					serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
							SerialPort.STOPBITS_2, SerialPort.PARITY_NONE);

					out = serialPort.getOutputStream();
				} else {
					System.out
							.println("Error: Only serial ports are handled by this example.");
				}
			}
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null,
					"Houve um erro ao acessar a serial(" + portName + "): " + e.toString());
		}
	}

	public static String bytesToHexString(byte[] bytes) {
		boolean zeros = true;
		StringBuilder sb = new StringBuilder(bytes.length * 2);

		Formatter formatter = new Formatter(sb);
		for (byte b : bytes) {
			formatter.format("%02x ", b);
			if (b != 0) {
				zeros = false;
			}
		}
		if (zeros) {
			return null;
		} else {
			return sb.toString();
		}
	}

	public static byte[] checkRobokit() {
		return packet(0x80, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00 });
	}

	public static byte[] ack() {
		return packet(0x81, new byte[] { 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
				0x06, 0x06 });
	}

	public static byte[] nck() {
		return packet(0x81, new byte[] { 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
				0x15, 0x15, 0x15 });
	}

	public static byte[] stepperMotorLeft(int pulse, int speed) {
		return stepperMotorLeft(pulse, speed, false);
	}

	public static byte[] stepperMotorLeft(int pulse, int speed, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x01,
				(byte) (pulse >> 8), (byte) (pulse), (byte) (speed >> 8),
				(byte) (speed) });
	}

	public static byte[] stepperMotorRight(int pulse, int speed) {
		return stepperMotorRight(pulse, speed, false);
	}

	public static byte[] stepperMotorRight(int pulse, int speed, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x02,
				(byte) (pulse >> 8), (byte) (pulse), (byte) (speed >> 8),
				(byte) (speed) });
	}

	public static byte[] continuousMotor1(int seconds, int speed) {
		return continuousMotor1(seconds, speed, false);
	}

	public static byte[] continuousMotor1(int seconds, int speed, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x04, 0x01,
				(byte) (speed), (byte) (seconds), 0x31 });
	}

	public static byte[] continuousMotor2(int seconds, int speed) {
		return continuousMotor2(seconds, speed, false);
	}

	public static byte[] continuousMotor2(int seconds, int speed, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x04, 0x02,
				(byte) (speed), (byte) (seconds), 0x32 });
	}

	public static byte[] led1(int seconds) {
		return led1(seconds, false);
	}

	public static byte[] led1(int seconds, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x08, 0x01,
				(byte) (seconds), 0x31, 0x00 });
	}

	public static byte[] led2(int seconds) {
		return led2(seconds, false);
	}

	public static byte[] led2(int seconds, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x08, 0x02,
				(byte) (seconds), 0x31, 0x00 });
	}

	public static byte[] led3(int seconds) {
		return led3(seconds, false);
	}

	public static byte[] led3(int seconds, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x08, 0x04,
				(byte) (seconds), 0x31, 0x00 });
	}

	public static byte[] led4(int seconds) {
		return led4(seconds, false);
	}

	public static byte[] led4(int seconds, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x08, 0x08,
				(byte) (seconds), 0x31, 0x00 });
	}

	public static byte[] sound(int seconds) {
		return sound(seconds, false);
	}

	public static byte[] sound(int seconds, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x08,
				(byte) (seconds >> 8), (byte) (seconds), 0x00, 0x00 });
	}

	public static byte[] loopStart(int seconds) {
		return loopStart(seconds, false);
	}

	public static byte[] loopStart(int seconds, boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x09,
				(byte) (seconds >> 8), (byte) (seconds), 0x00, 0x00 });
	}

	public static byte[] loopEnd() {
		return loopEnd(false);
	}

	public static byte[] loopEnd(boolean save) {
		return packet(!save ? 0x82 : 0x84, new byte[] { 0x00, 0x00, 0x00, 0x00,
				0x00 });
	}

	public static byte[] cleanMemory() {
		return packet(0x83, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00 });
	}

	public static byte[] robokitSerialNumber() {
		return packet(0x85, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00 });
	}

	public static byte[] runList() {
		return packet(0x86, new byte[] { 0x00 });
	}

	public static byte[] packet(int cmd, byte[] data) {
		byte[] d = new byte[data.length + 8];
		d[0] = 0x01;
		d[1] = 0x55;
		d[2] = 0x41;
		d[3] = 0x00;
		d[4] = (byte) (8 + data.length);
		d[5] = 0x40;
		d[6] = (byte) cmd;
		int x;
		for (x = 0; x < data.length; x++) {
			d[x + 7] = data[x];
		}
		int crc = 0;
		for (x = 1; x < d.length - 1; x++) {
			crc += d[x];
		}
		d[x] = (byte) ~crc;
		return d;
	}
}
